<template>
  <div class="container">
    <!-- 数据表格 -->
    <el-table
      ref="envTblRef"
      :data="tableData"
      height="100%"
      border
      header-cell-class-name="envHeaderClass"
      cell-class-name="envCellClass"
      @cell-click="handleCellClick"
      @selection-change="handleSelectionChange"
    >
      <el-table-column type="selection" width="55" />
      <el-table-column
        v-for="header in envHeaders"
        :key="header.prop"
        :prop="header.prop"
        :label="header.label"
        :width="header.width"
      >
        <template v-slot="scope">
          <el-form ref="envForm" :model="scope.row" :rules="envRules">
            <div v-if="header.isSelected || header.isSwitch">
              <el-form-item :prop="header.prop">
                <el-switch
                  v-if="header.isSwitch"
                  v-model="scope.row[header.prop]"
                  active-text="是"
                  inactive-text="否"
                  active-value="1"
                  inactive-value="0"
                />
                <el-select
                  v-else
                  v-model="scope.row[header.prop]"
                  :disabled="header.prop === 'envType'? false: scope.row['envType']!==$cons.ENV_TYPE_DB"
                  placeholder="请选择"
                >
                  <el-option
                    v-for="item in header.prop === 'envType'? envTypeOptions : dbTypeOptions"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value"
                  />
                </el-select>
              </el-form-item>
            </div>
            <div v-else>
              <el-form-item :prop="header.prop">
                <el-input
                  v-if="isEditing(scope.$index, header.prop)"
                  v-model="scope.row[header.prop]"
                  :show-password="header.prop === 'password'"
                  @blur="saveEdit(scope.$index)"
                  @focus="readyInput(header)"
                />
                <div v-else>
                  <span v-if="header.prop === 'password'">
                    {{ scope.row[header.prop] === null ? '' : '●'.repeat(scope.row[header.prop].length || 0) }}
                  </span>
                  <span v-else-if="header.isStatus">
                    <el-tooltip
                      effect="dark"
                      :content="aLiveExtraInfo(scope.row, projectStatus).srcTip.tip"
                      placement="top-start"
                    >
                      <i class="iconfont" :class="aLiveExtraInfo(scope.row, projectStatus).srcTip.iconSrc" />
                    </el-tooltip>
                  </span>
                  <span v-else>{{ scope.row[header.prop] }}</span>
                </div>
              </el-form-item>
            </div>
          </el-form>
        </template>
      </el-table-column>
    </el-table>

    <!-- 操作按钮列 -->
    <div class="operation-btns">
      <div v-if="parentBody === 'proInfo'" style="margin-top: 10px;float: right">
        <el-tooltip class="item" effect="dark" content="增加记录" placement="bottom">
          <el-button size="mini" type="info" icon="el-icon-plus" circle @click="addRow" />
        </el-tooltip>
        <el-tooltip class="item" effect="dark" content="删除记录" placement="bottom">
          <el-button size="mini" type="info" icon="el-icon-minus" circle @click="deleteRow" />
        </el-tooltip>
        <el-tooltip class="item" effect="dark" content="应用更改" placement="bottom">
          <el-button size="mini" type="info" icon="el-icon-check" circle @click="submit" />
        </el-tooltip>
        <el-tooltip class="item" effect="dark" content="刷新" placement="bottom">
          <el-button size="mini" type="info" icon="el-icon-refresh" circle @click="handleRefresh" />
        </el-tooltip>
      </div>
      <div v-else style="display: flex;justify-content: center;align-items: center; margin-top: 10px;">
        <el-button size="mini" type="primary" @click="compEnvSet">提交</el-button>
        <el-button size="mini" @click="cancelSet">取消</el-button>
      </div>
    </div>
  </div>
</template>

<script>
import { mapGetters } from 'vuex'
import { constants as $cons } from '../../../../store/constants'
import { envNetStatus } from '@/utils/utils'
import { getProjectEnvs, updateEnvList } from '@/api/project'
import { getComponentEnvList } from '@/api/component'

export default {
  name: 'ProjectEnvInfo',
  props: {
    // 父级页面：项目信息点击,proInfo; 组件绑定点击, compBan
    // 项目信息点击时，环境信息的列是全的；组件绑定点击只需要显示IP和网络状态
    parentBody: {
      type: String,
      required: true
    },

    // 组件ID，当且仅当parentBody为compBan时不为空
    compBanEnvHosts: {
      type: Array,
      default: () => []
    }
  },
  data() {
    return {
      envInfoList: [],
      tableData: [], // 当前表格数据
      originalData: [], // 原始数据备份
      editing: { // 编辑状态
        rowIndex: -1,
        column: null
      },
      // 环境类型下拉选项
      envTypeOptions: [{
        value: this.$const.ENV_TYPE_CALC,
        label: '计算节点'
      }, {
        value: this.$const.ENV_TYPE_DB,
        label: '数据库节点'
      }, {
        value: this.$const.ENV_TYPE_APP,
        label: '应用节点'
      }],
      // 数据库类型下拉选项
      dbTypeOptions: [{
        value: this.$const.DB_TYPE_MYSQL,
        label: 'Mysql'
      }, {
        value: this.$const.DB_TYPE_ORACLE,
        label: 'Oracle'
      }],
      // 选中的行
      selectedRows: [],
      envHeaders: [],
      aliveStatus: {},
      envRules: {
        host: [{
          required: true,
          trigger: 'blur',
          validator: (rule, value, callback) => {
            if (!value) {
              return callback(new Error('环境IP不能为空'))
            }
            if (/^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$/.test(value)) {
              return callback()
            } else {
              return callback(new Error('请输入正确的IPv4地址'))
            }
          }
        }],
        port: [{
          required: true,
          trigger: 'blur',
          validator: (rule, value, callback) => {
            if (!value) {
              return callback(new Error('端口号不能为空'))
            }
            if (/^([1-9](\d{0,3}))$|^([1-5]\d{4})$|^(6[0-4]\d{3})$|^(65[0-4]\d{2})$|^(655[0-2]\d)$|^(6553[0-5])$/.test(value)) {
              return callback()
            } else {
              return callback(new Error('端口号范围：1~65535'))
            }
          }
        }],
        username: [
          { required: true, message: '用户名不能为空', trigger: 'blur' },
          { min: 3, max: 32, message: '长度在 3 到 32 个字符', trigger: 'blur' }
        ],
        password: [{
          required: true, trigger: 'blur', message: '密码不能为空'
        }]
      },
      // 当前列的宽度，用来处理输入时宽度变大，失去焦点时还原
      curCellWidth: {}
    }
  },
  computed: {
    $cons() {
      return $cons
    },
    ...mapGetters([
      'projectStatus'
    ])
  },
  emits: ['pro-env-submit', 'comp-env-submit', 'comp-env-cancel'],

  mounted() {
    // 查询项目所有的环境信息
    this.initEnvList()
  },

  methods: {
    initEnvList() {
      getProjectEnvs({ projectId: this.$route.meta.projectId }).then(res => {
        const list = res.data
        if (!list) {
          throw new Error('获取环境信息失败！！')
        }
        this.envInfoList = list
        this.initEnvHeaders()
        const cpEnvInfo = []
        if (this.envInfoList) {
          this.envInfoList.forEach(e => {
            e.needSshTunnel = '' + e.needSshTunnel
            cpEnvInfo.push({ ...e })
          })
        }
        this.tableData = JSON.parse(JSON.stringify(cpEnvInfo))
        this.originalData = JSON.parse(JSON.stringify(cpEnvInfo))
        if (this.parentBody === 'compBan') {
          // 若父级组件为绑定组件则查询组件的环境信息
          this.$nextTick(() => {
            this.tableData.filter(row => {
              return this.compBanEnvHosts.includes(row.host)
            }).forEach(row => {
              this.$refs.envTblRef.toggleRowSelection(row, true)
            })
          })
        }
      }).catch(err => {
        console.error(err)
        this.$message.error('获取项目环境信息失败！！')
      })
    },

    initEnvHeaders() {
      if (this.parentBody === 'compBan') {
        this.envHeaders = [{
          prop: 'host',
          label: '环境IP'
        }, {
          prop: 'status',
          label: '环境状态',
          isStatus: true
        }]
      } else {
        this.envHeaders = [{
          prop: 'envType',
          label: '环境类型',
          isSelected: true,
          width: '140px'
        }, {
          prop: 'host',
          label: '环境IP'
        }, {
          prop: 'port',
          label: '端口号',
          width: '70px'
        }, {
          prop: 'username',
          label: '用户名'
        }, {
          prop: 'password',
          label: '密码'
        }, {
          prop: 'dbType',
          label: '数据库类型',
          isSelected: true
        }, {
          prop: 'needSshTunnel',
          label: '是否需要使用SSH隧道',
          isSwitch: true
        }, {
          prop: 'status',
          label: '环境状态',
          isStatus: true,
          width: '80px'
        }]
      }
    },

    // 添加新行
    addRow() {
      this.tableData.push({
        projectId: this.$route.meta.projectId,
        envId: '',
        envType: this.$const.ENV_TYPE_CALC,
        host: '',
        port: '22',
        username: '',
        password: '',
        dbType: '',
        needSshTunnel: '1',
        isAlive: '1',
        srcTip: { iconSrc: 'icon-xitongchushihua', tip: '环境初始化' },
        isNew: true
      })
    },

    // 删除选中行
    deleteRow() {
      this.selectedRows.forEach(row => {
        const index = this.tableData.findIndex(item => item.id === row.id)
        if (index > -1) {
          this.tableData.splice(index, 1)
        }
      })
    },

    // 准备输入内容
    readyInput(header) {
      if (this.parentBody !== 'proInfo') {
        return
      }
      this.envHeaders.forEach(e => {
        this.curCellWidth[e.prop] = e.width
        if (e.prop !== header.prop) {
          e.width = undefined
        } else {
          e.width = '200px'
        }
      })
    },

    // 提交所有修改
    async submit() {
      let isOk = true
      // 提交后端并更新父组件数据
      this.$refs['envForm'].forEach(e => {
        e.validate((valid) => {
          if (!valid) {
            isOk &= false
            return false
          }
          isOk &= true
        })
      })
      if (!isOk) {
        return
      }

      // 若未选中任何行，则提交所有，若选中则以选中的行提交
      this.updateEnvList(this.selectedRows.length > 0 ? this.selectedRows.length : this.tableData)
    },

    updateEnvList(envList) {
      updateEnvList({ projectId: this.$route.meta.projectId, envList: envList }).then(res => {
        this.$message.success('提交成功')
        this.originalData = JSON.parse(JSON.stringify(this.tableData))
        this.$emit('pro-env-submit')
      }).catch(err => {
        console.error(err)
        this.$message.error('提交失败')
      })
    },

    // 处理刷新
    handleRefresh() {
      this.$confirm('刷新而不应用?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '应用',
        type: 'warning'
      }).then(() => {
        this.tableData = JSON.parse(JSON.stringify(this.originalData))
      }).catch(() => {
        this.submit().then(() => this.updateEnvList(this.tableData))
      })
    },

    // 单元格点击处理
    handleCellClick(row, column, cell, event) {
      if (this.parentBody !== 'proInfo') {
        return
      }
      const columns = ['envType', 'host', 'port', 'username', 'password', 'dbType', 'needSshTunnel']
      if (columns.includes(column.property)) {
        this.editing = {
          rowIndex: this.tableData.indexOf(row),
          column: column.property
        }
      }
    },

    // 判断是否在编辑状态
    isEditing(index, column) {
      return this.editing.rowIndex === index && this.editing.column === column
    },

    // 保存编辑
    saveEdit(index) {
      this.editing = { rowIndex: -1, column: null }
      if (!this.tableData[index].isNew) {
        this.tableData[index].isModified = true
      }
      this.envHeaders.forEach(e => {
        e.width = this.curCellWidth[e.prop]
      })
    },

    // 选中行变化
    handleSelectionChange(rows) {
      this.selectedRows = rows
    },

    aLiveExtraInfo(row, stateProStatus) {
      const isAlive = envNetStatus(row.projectId, row.id, stateProStatus)
      return {
        srcTip: isAlive ? { iconSrc: 'icon-wangluozhengchang', tip: '网络正常' }
          : { iconSrc: 'icon-wangluoyichang', tip: '网络异常' }
      }
    },
    // 组件环境设置
    compEnvSet() {
      this.$emit('comp-env-submit', this.selectedRows)
    },
    // 取消设置
    cancelSet() {
      this.$emit('comp-env-cancel', false)
    }
  }
}
</script>

<style scoped>
.container {
  position: relative;
  height: 60vh;
  overflow: auto;
  padding: 4px;
  box-sizing: border-box;
}

/* el-table高度自适应 */
.container >>> .el-table {
  /* 预留底部空间‌ */
  height: calc(100% - 50px) !important;
}

.operation-btns {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  background: #fff;
  padding: 4px;
  box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.1);
  z-index: 2000;
}

.container >>> .envHeaderClass {
  text-align: center;
  background: #EFE3C8;
  color: #000000;
}

.container >>> .envCellClass {
  text-align: center;
}

.icon-wangluozhengchang {
  color: #00ff33;
}

.icon-wangluoyichang {
  color: #FF7272;
  font-size: 20px;
}
</style>
